﻿using System;
using QQ2564874169.Core.Caching;
using StackExchange.Redis;

namespace QQ2564874169.Cache.Redis
{
    public class RedisCache : ICache
    {
        public interface ISerialization
        {
            object Deserialize(Type type, string data);
            string Serialize(object obj);
        }
        public static int? DefaultExpiration { get; set; }

        private IDatabase _db;
        private ISerialization _serialization;

        public RedisCache(IDatabase db, ISerialization serialization = null)
        {
            _db = db;
            _serialization = serialization ?? new JsonSerialization();
        }

        public void Save(CacheItem data)
        {
            if (string.IsNullOrEmpty(data.Key))
                throw new ArgumentException("key不能为空。");
            if (data.Value == null)
                throw new ArgumentException("value不能为空。");
            var item = new InternalCacheItem
            {
                Key = data.Key,
                Value = _serialization.Serialize(data.Value),
                ValueType = data.Value.GetType().GetAssemblyQualifiedName(),
                AbsoluteExpiration = data.AbsoluteExpiration,
                SlidingExpiration = data.SlidingExpiration
            };
            _db.StringSet(data.Key, _serialization.Serialize(item), item.GetExpiry(DefaultExpiration));
        }

        public void Update(string key, object value)
        {
            var json = _db.StringGet(key);
            if (json.IsNullOrEmpty)
                return;
            var item = (InternalCacheItem)_serialization.Deserialize(typeof(InternalCacheItem), json);
            item.Value = _serialization.Serialize(value);
            item.ValueType = value.GetType().GetAssemblyQualifiedName();
            _db.StringSet(item.Key, _serialization.Serialize(item), item.GetExpiry(DefaultExpiration));
        }

        public bool Exists(string key)
        {
            return _db.KeyExists(key);
        }

        public object Get(string key)
        {
            var json = _db.StringGet(key);
            if (json.IsNullOrEmpty)
                return null;
            var item = (InternalCacheItem)_serialization.Deserialize(typeof(InternalCacheItem), json);
            if (item?.Value == null || item.Key == null)
                return null;

            var t = Type.GetType(item.ValueType, false);
            object v;
            if (t == null)
            {
                v = item.Value;
            }
            else if (t == typeof(bool))
            {
                v = bool.Parse(item.Value);
            }
            else
            {
                v = _serialization.Deserialize(t, item.Value);
            }
            if (item.SlidingExpiration.HasValue && item.AbsoluteExpiration.HasValue == false)
            {
                _db.KeyExpire(item.Key, item.GetExpiry(DefaultExpiration));
            }
            return v;
        }

        public void Remove(string key)
        {
            _db.KeyDelete(key);
        }
    }
}
